﻿using System;
using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

public static class EditorCoroutineRunner
{
	private class EditorCoroutine :IEnumerator
	{
		private Stack<IEnumerator> executionStack;

		public EditorCoroutine(IEnumerator iterator)
		{
			this.executionStack = new Stack<IEnumerator>();
			this.executionStack.Push(iterator);
		}

		public bool MoveNext()
		{
			IEnumerator i = this.executionStack.Peek();

			if (i.MoveNext())
			{
				object result = i.Current;
				if (result != null && result is IEnumerator)
				{
					this.executionStack.Push((IEnumerator)result);
				}

				return true;
			}
			else
			{
				if (this.executionStack.Count > 1)
				{
					this.executionStack.Pop();
					return true;
				}
			}

			return false;
		}

		public void Reset()
		{
			throw new System.NotSupportedException("This Operation Is Not Supported.");
		}

		public object Current
		{
			get
			{
				return this.executionStack.Peek().Current;
			}
		}

		public bool Find(IEnumerator iterator)
		{
			return this.executionStack.Contains(iterator);
		}
	}

	private static List<EditorCoroutine> editorCoroutineList;
	private static List<IEnumerator> buffer;

	public static IEnumerator StartEditorCoroutine(IEnumerator iterator)
	{
		if (editorCoroutineList == null)
		{
			// test  
			editorCoroutineList = new List<EditorCoroutine>();
		}
		if (buffer == null)
		{
			buffer = new List<IEnumerator>();
		}
		if (editorCoroutineList.Count == 0)
		{
			EditorApplication.update += Update;
		}

		// add iterator to buffer first  
		buffer.Add(iterator);

		return iterator;
	}

	/// <summary>
	/// 重载版本，协程中捕获异常或者正常完成时会回调
	/// 注意目前没处理协程嵌套协程，此时需要每次开新协程都单独捕获异常
	/// </summary>
	/// <param name="iterator">开启的协程</param>
	/// <param name="exceptionCallback">异常回调</param>
	/// <returns></returns>
	public static IEnumerator StartEditorCoroutine(IEnumerator iterator, Action<Exception> exceptionCallback)
	{
		return StartEditorCoroutine(CoroutineWithExceptionHandling(iterator, exceptionCallback));
	}

	private static bool Find(IEnumerator iterator)
	{
		// If this iterator is already added  
		// Then ignore it this time  
		foreach (EditorCoroutine editorCoroutine in editorCoroutineList)
		{
			if (editorCoroutine.Find(iterator))
			{
				return true;
			}
		}

		return false;
	}

	private static void Update()
	{
		// EditorCoroutine execution may append new iterators to buffer  
		// Therefore we should run EditorCoroutine first  
		editorCoroutineList.RemoveAll
		(
			coroutine =>
			{
				return coroutine.MoveNext() == false;
			}
		);

		// If we have iterators in buffer  
		if (buffer.Count > 0)
		{
			foreach (IEnumerator iterator in buffer)
			{
				// If this iterators not exists  
				if (!Find(iterator))
				{
					// Added this as new EditorCoroutine  
					editorCoroutineList.Add(new EditorCoroutine(iterator));
				}
			}

			// Clear buffer  
			buffer.Clear();
		}

		// If we have no running EditorCoroutine  
		// Stop calling update anymore  
		if (editorCoroutineList.Count == 0)
		{
			EditorApplication.update -= Update;
		}
	}
	
	/// <summary>
	/// 包装一层协程try catch便于协程抛出异常时进程退出
	/// 参考 https://www.jacksondunstan.com/articles/3718
	/// </summary>
	/// <param name="coroutine">传入协程</param>
	/// <param name="doneCallback">结束回调，回调非null时表示有异常，null时无异常</param>
	/// <returns></returns>
	public static IEnumerator CoroutineWithExceptionHandling(IEnumerator coroutine, Action<Exception> doneCallback=null)
	{
		while (true)
		{
			object current;
			try
			{
				if (!coroutine.MoveNext())
				{
					// 正常结束，则结束循环
					break;
				}
				current = coroutine.Current;
			}
			catch (Exception ex)
			{
				//有异常时，回调错误信息，并break协程
				doneCallback?.Invoke(ex);
				yield break;
			}
			yield return current;
		}
		// 执行到正常处理结束，回调时返回null表示无异常
		doneCallback?.Invoke(null);
	}
}